{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Autotranslation Groovy to JavaScript and D3\n",
    "\n",
    "Generate a random graph with Groovy, then visualize it with a [D3](http://d3js.org/) interactive, force-directed graph.\n",
    "\n",
    "First we generate the graph (one made of nodes and edges, like a social network graph)\n",
    "and store it in the BeakerX object.\n",
    "\n",
    "Then we load D3 and set its styles.\n",
    "\n",
    "Finally, a JavaScript cell gets the data from the BeakerX object and renders it with D3.\n",
    "\n",
    "This final cell was\n",
    "copied almost verbatim from the [D3 documentation](http://bl.ocks.org/mbostock/4062045).  Other D3 examples\n",
    "should be similarly easy to get working in BeakerX."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def r = new Random()\n",
    "def nnodes = 100\n",
    "def nodes = []\n",
    "def links = []\n",
    "\n",
    "for (x in (0..nnodes)){\n",
    "  nodes.add(name:\"\" + x, group:((int) x*7/nnodes))\n",
    "}\n",
    "\n",
    "for (x in (0..(int) nnodes*1.15)) { \n",
    "  source = x % nnodes\n",
    "  target = ((int) log(1 + r.nextInt(nnodes))/log(1.3))\n",
    "  value = 10.0 / (1 + abs(source - target))\n",
    "  links.add(source: source, target: target, value: value*value)\n",
    "}\n",
    "\n",
    "beakerx.graph = [nodes: nodes, links: links]\n",
    "OutputCell.HIDDEN"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%%javascript\n",
    "require.config({\n",
    "  paths: {\n",
    "      d3: '//cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min'\n",
    "  }});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%%html\n",
    "<style>\n",
    ".node {\n",
    "  stroke: #fff;\n",
    "  stroke-width: 1.5px;\n",
    "}\n",
    "\n",
    ".link {\n",
    "  stroke: #999;\n",
    "  stroke-opacity: .6;\n",
    "}\n",
    "</style>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%%javascript\n",
    "\n",
    "beakerx.displayHTML(this, '<div id=\"fdg\"></div>');\n",
    "\n",
    "var graph = beakerx.graph\n",
    "\n",
    "var d3 = require(['d3'], function (d3) {\n",
    "    \n",
    "    var width = 600,\n",
    "        height = 500;\n",
    "\n",
    "    var color = d3.scaleOrdinal(d3.schemeCategory20);\n",
    "\n",
    "    var simulation = d3.forceSimulation()\n",
    "        .force(\"link\", d3.forceLink().distance(30))\n",
    "        .force(\"charge\", d3.forceManyBody().strength(-200))\n",
    "        .force(\"center\", d3.forceCenter(width / 2, height / 2))\n",
    "        .force(\"y\", d3.forceY(width / 2).strength(0.3))\n",
    "        .force(\"x\", d3.forceX(height / 2).strength(0.3));\n",
    "\n",
    "    var svg = d3.select(\"#fdg\")\n",
    "                .append(\"svg\")\n",
    "                .attr(\"width\", width)\n",
    "                .attr(\"height\", height)\n",
    "                .attr(\"transform\", \"translate(\"+[100, 0]+\")\");\n",
    "\n",
    "    simulation\n",
    "          .nodes(graph.nodes)\n",
    "          .force(\"link\")\n",
    "          .links(graph.links);\n",
    "\n",
    "    var link = svg.selectAll(\".link\")\n",
    "          .data(graph.links)\n",
    "          .enter().append(\"line\")\n",
    "          .attr(\"class\", \"link\")\n",
    "          .style(\"stroke-width\", function(d) { return Math.sqrt(d.value); });\n",
    "\n",
    "    var node = svg.selectAll(\".node\")\n",
    "          .data(graph.nodes)\n",
    "          .enter().append(\"circle\")\n",
    "          .attr(\"class\", \"node\")\n",
    "          .attr(\"r\", 10)\n",
    "          .style(\"fill\", function(d) { return color(d.group); });\n",
    "\n",
    "    node.append(\"title\")\n",
    "          .text(function(d) { return d.name; });\n",
    "\n",
    "    simulation.on(\"tick\", function() {\n",
    "        link.attr(\"x1\", function(d) { return d.source.x; })\n",
    "            .attr(\"y1\", function(d) { return d.source.y; })\n",
    "            .attr(\"x2\", function(d) { return d.target.x; })\n",
    "            .attr(\"y2\", function(d) { return d.target.y; });\n",
    "\n",
    "        node.attr(\"cx\", function(d) { return d.x; })\n",
    "            .attr(\"cy\", function(d) { return d.y; });\n",
    "    });\n",
    "    \n",
    "    node.call(d3.drag()\n",
    "        .on(\"start\", dragstarted)\n",
    "        .on(\"drag\", dragged)\n",
    "        .on(\"end\", dragended)\n",
    "    );\n",
    "    \n",
    "    function dragstarted(d) {\n",
    "        if (!d3.event.active) simulation.alphaTarget(0.3).restart();\n",
    "        d.fx = d.x;\n",
    "        d.fy = d.y;\n",
    "    }\n",
    "\n",
    "    function dragged(d) {\n",
    "        d.fx = d3.event.x;\n",
    "        d.fy = d3.event.y;\n",
    "    }\n",
    "\n",
    "    function dragended(d) {\n",
    "        if (!d3.event.active) simulation.alphaTarget(0);\n",
    "        d.fx = null;\n",
    "        d.fy = null;\n",
    "    }\n",
    "});"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "beakerx_kernel_parameters": {},
  "classpath": [],
  "imports": [],
  "kernelspec": {
   "display_name": "Groovy",
   "language": "groovy",
   "name": "groovy"
  },
  "language_info": {
   "codemirror_mode": "groovy",
   "file_extension": ".groovy",
   "mimetype": "",
   "name": "Groovy",
   "nbconverter_exporter": "",
   "version": "2.4.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
